package com.mrogrape.notepad.ui.library;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import com.jidesoft.swing.ResizablePanel;
import com.mrogrape.notepad.common.ExitActionHandler;
import com.mrogrape.notepad.common.NotepadUtils;
import com.mrogrape.notepad.common.ViewUtils;
import com.mrogrape.notepad.data.ApplicationContext;
import com.mrogrape.notepad.data.ViewContext;
import com.mrogrape.notepad.resource.Settings;

public class LibraryPane extends ResizablePanel {

	/** serialVersionUID */
	private static final long serialVersionUID = 8862161388762853693L;

	/**
	 * 创建库视图，并指定库列表。
	 * 
	 * @param libraries
	 */
	public LibraryPane() {

		this.librariesManager = new LibrariesManager(Settings.getSettingsFolder(),"libraries.xml");
		try {
			this.libraries = this.librariesManager.load();
		}
		catch (Exception e) {
			e.printStackTrace();
			this.libraries = new LinkedList<Library>();
		}
		this.exitActionHandler = new LibraryExitActionHandler(this);

		System.out.println(this.libraries);

		this.initComponents();
		ApplicationContext.exitActionHandlers.add(this.exitActionHandler);

	}


	/**
	 * 构建整个库视图的结构，此方法可能消耗较多的时间。
	 * 
	 * <p>
	 * 此方法是线程安全的。
	 */
	public synchronized void buildStructure() {

		if (this.libraries.size() == 0) {
			return;
		}
		this.rootNode.removeAllChildren();

		for (Library lib : this.libraries) {

			LibraryNode libNode = new LibraryNode(lib);
			List<Inclusion> includes = lib.getIncludes();

			for (Inclusion inclusion : includes) {
				
				if(Inclusion.FILE.equalsIgnoreCase(inclusion.getType())){
					
					File file = new File(inclusion.getHref());
					FileInclusionNode node = new FileInclusionNode(inclusion);
					if (file.exists() && file.isDirectory()) {

						File[] sFiles = file.listFiles();
						for (File sFile : sFiles) {
							this.addFileToNode(node, sFile);
						}
					}
					libNode.add(node);
					this.sortFileItemNode(libNode);
				}
				else if(Inclusion.URL.equalsIgnoreCase(inclusion.getType())){
					libNode.add(new URLInclusionNode(inclusion));
				}
			}
			this.rootNode.add(libNode);
		}

		EventQueue.invokeLater(new Runnable() {

			@Override
			public void run() {

				LibraryPane.this.treeModel.reload();
				LibraryPane.this.validate();
				LibraryPane.this.repaint();
			}
		});
	}

	public boolean save(){

		try {
			this.librariesManager.save(this.libraries);
		}
		catch (FileNotFoundException e) {
			e.printStackTrace();
			ViewUtils.showErrorMessageDialog(null, e.getMessage());
			return false;
		}
		catch (ParserConfigurationException e) {
			e.printStackTrace();
			ViewUtils.showErrorMessageDialog(null, e.getMessage());
			return false;
		}
		catch (TransformerException e) {
			e.printStackTrace();
			ViewUtils.showErrorMessageDialog(null, e.getMessage());
			return false;
		}

		return true;
	}

	/**
	 * 往树视图中添加一个库。
	 * 
	 * @param library
	 */
	public void addLibrary(Library library) {

		if (library == null) {
			throw new NullPointerException("Library cannot be null");
		}

		if (this.isLibraryExist(library)) {
			return;
		}

		this.libraries.add(library);

		LibraryNode node = new LibraryNode(library);
		this.treeModel.insertNodeInto(node, this.rootNode, this.rootNode.getChildCount());
		this.makeNodeVisible(node);
	}

	/**
	 * 从库视图中移除一个库。
	 * 
	 * @param library
	 */
	public void removeLibrary(Library library){
		if (library == null) {
			return;
		}

		if (!this.isLibraryExist(library)) {
			return;
		}

		this.libraries.remove(library);

		MutableTreeNode found = this.libraryToNode(library);

		if(found != null){

			this.treeModel.removeNodeFromParent(found);
		}
	}

	/**
	 * 获取当前选择的节点数目。
	 * 
	 * @return
	 */
	public int getSelectionNodeCount(){

		return this.tree.getSelectionCount();
	}

	/**
	 * 获取当前选择的库的数目。
	 * 
	 * @return
	 */
	public int getSelectedLibrariesCount(){

		return this.getSelectedLibraries().length;
	}

	/**
	 * 获取选中的库对象，如果选择的多个节点中存在多个库，则返回第一个。
	 * @return
	 */
	public Library getSelectedLibrary(){

		if(this.getSelectionNodeCount() == 0){ return null; }

		int[] rows = this.tree.getSelectionRows();
		for(int row : rows){
			TreePath path = this.tree.getPathForRow(row);
			Library found = this.pathToLibrary(path);
			if(found != null){
				return found;
			}
		}

		return null;
	}
	
	/**
	 * 获取选中节点中的所有库对象。
	 * 
	 * @return 
	 */
	public Library[] getSelectedLibraries(){

		List<Library> libraries = new ArrayList<Library>();
		if(this.getSelectionNodeCount() == 0){ return null; }

		int[] rows = this.tree.getSelectionRows();
		for(int row : rows){
			TreePath path = this.tree.getPathForRow(row);
			Library found = this.pathToLibrary(path);
			if(found != null){
				libraries.add(found);
			}
		}

		return libraries.toArray(new Library[0]);
	}

	/**
	 * 获取包含着根导入对象的库。如果严格模式为true，则返回包含该对象的库对象，反正则返回包含
	 * 与对象相等的库对象。
	 * 
	 * @param inclusion
	 * @param strict
	 * @return
	 */
	public Library getLibraryContainsRootInclusion(Inclusion inclusion, boolean strict){

		if(strict){
			for(Library library : this.libraries){
				for(Inclusion selInclusion : library.getIncludes()){
					if(selInclusion == inclusion){
						return library;
					}
				}
			}
		}
		// TODO ELSE

		return null;
	}

	/**
	 * 获取选中的导入对象，如果没有选中的导入对象则返回空，如果有多个则返回第一个。
	 * 
	 * @return
	 */
	public Inclusion getSelectedInclusion(){

		if(this.getSelectionNodeCount() == 0){ return null; }

		Object nodeobj = this.tree.getSelectionPath().getLastPathComponent();
		if(nodeobj instanceof InclusionNode){

			return ((InclusionNode)nodeobj).getInclusion();
		}

		return null;
	}

	/**
	 * 判断库是否存在。
	 * 
	 * @param lib
	 * @return
	 */
	public boolean isLibraryExist(Library lib) {

		for (Library ext : this.libraries) {
			if (ext.equals(lib)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 查看某个库的名字是否已经存在。
	 * 
	 * @param name
	 * @return
	 */
	public boolean isLibraryNameExist(String name){
		for (Library ext : this.libraries) {
			if (ext.getName().equals(name)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 查看是否是库中的根导入对象。
	 * 
	 * @param inclusion
	 * @return
	 */
	public boolean isRootInclusion(Inclusion inclusion){

		for(Library lib : this.libraries){
			LibraryNode libNode = this.libraryToNode(lib);
			int cnt = libNode.getChildCount();
			for(int i =0; i<cnt; i++){
				InclusionNode subNode = (InclusionNode) libNode.getChildAt(i);
				if(subNode.getInclusion().equals(inclusion)){
					return true;
				}
			}
		}

		return false;
	}

	/**
	 * 移除一个根级别的导入对象。如果指定对象不属于指定的库或不是根级别的导入对象，则不作任何操作。
	 * 
	 * @param lib
	 * @param inclusion
	 */
	public void removeRootInclusionFromLibrary(Library lib, Inclusion inclusion){

		if(lib == null || inclusion == null){
			return;
		}

		if(!this.isLibraryExist(lib)){
			return;
		}

		LibraryNode libNode = this.libraryToNode(lib);
		Library srcLib = libNode.getLibrary();

		Inclusion foundInclusion = srcLib.finInclusion(inclusion.getName());
		if(foundInclusion == null){
			return;
		}

		srcLib.removeInclusion(inclusion.getName());
		this.treeModel.removeNodeFromParent(this.inclusionToNode(libNode, foundInclusion));
	}

	/**
	 * 获取指定位置节点的用户对象。
	 * 
	 * @param x 
	 * @param y
	 * @return
	 */
	public Object getObjectForLocation(int x, int y){

		return ((DefaultMutableTreeNode)this.getNodeForLocation(x, y)).getUserObject();
	}


	/**
	 * 导入一个对象到库中。
	 * 
	 * @param lib
	 * @param inclusion
	 */
	public void includeInclusion(Library lib, Inclusion inclusion){

		if(LibraryUtils.isFileInclusion(inclusion)){


			System.out.println("Add file inclusion " + inclusion.getName() + "to " + lib.getName());// TODO remove if release
			this.includeFileInclusion(lib, inclusion);
		}
		else{
			this.includeURLInclusion(lib,inclusion);
		}
	}

	/**
	 * 指定的坐标是否在一个节点上。
	 * 
	 * @param x
	 * @param y
	 * @return
	 */
	public boolean isLocationOnNode(int x, int y) {

		return LibraryPane.this.tree.getRowForLocation(x, y) != -1;
	}
	
	/**
	 * 刷新当前选中的文件（夹）节点
	 */
	public void refreshSelectedFileInclusion(){
		
		if(this.getSelectionNodeCount() > 1){
			return;
		}
		
		MutableTreeNode node = this.getSelectedNode();
		if(node != null && (node instanceof FileInclusionNode)){
			
			FileInclusionNode fileNode = (FileInclusionNode) node;
			File file = fileNode.getFile();
			
			fileNode.removeAllChildren();
			File[] sFiles = file.listFiles();
			for(File sFile : sFiles){
				this.addFileToNode(fileNode, sFile);
			}
			
			this.treeModel.nodeChanged(fileNode);
			this.treeModel.nodeStructureChanged(fileNode);
		}
	}

	/**
	 * 刷新指定的文件（夹）节点。
	 * 
	 * @param inclusion
	 */
	public void refreshFileInclusion(Library library, Inclusion inclusion){
		
		LibraryNode libNode = this.libraryToNode(library);
		if(libNode == null){
			return;
		}
		InclusionNode node = this.inclusionToNode(libNode, inclusion);
		if(node == null || !(node instanceof FileInclusionNode)){
			return;
		}
		
		FileInclusionNode fileNode = (FileInclusionNode) node;
		File file = fileNode.getFile();
		
		fileNode.removeAllChildren();
		File[] sFiles = file.listFiles();
		for(File sFile : sFiles){
			this.addFileToNode(fileNode, sFile);
		}
		
		this.treeModel.nodeChanged(fileNode);
		this.treeModel.nodeStructureChanged(fileNode);
	}

	private void includeURLInclusion(Library lib, Inclusion inclusion){
		if (lib == null || inclusion == null) {
			throw new NullPointerException("Argument cannot be null");
		}

		LibraryNode libNode = this.libraryToNode(lib);
		if (libNode == null) {
			return;
		}

		Library srcLib = libNode.getLibrary();
		srcLib.includeInclusion(inclusion);


		libNode.add(new URLInclusionNode(inclusion));
		this.treeModel.nodeStructureChanged(libNode);

	}

	/**
	 * 导入一个文件到库中。
	 * 
	 * @param lib
	 * @param name
	 * @param file
	 */
	private void includeFileInclusion(Library lib, Inclusion inclusion) {

		if (lib == null || inclusion == null) {
			throw new NullPointerException("Argument cannot be null");
		}

		LibraryNode libNode = this.libraryToNode(lib);
		if (libNode == null) {
			return;
		}

		Library srcLib = libNode.getLibrary();
		srcLib.includeInclusion(inclusion);

		FileInclusionNode newNode = new FileInclusionNode(inclusion);
		this.buildFileItemNode(newNode);

		libNode.add(newNode);
		this.treeModel.nodeStructureChanged(libNode);
	}

	private MutableTreeNode getNodeForLocation(int x, int y) {

		return (MutableTreeNode) this.tree.getPathForLocation(x, y).getLastPathComponent();
	}

	private MutableTreeNode getSelectedNode(){
		
		if(this.getSelectionNodeCount() == 0){
			return null;
		}
		return (MutableTreeNode) this.tree.getSelectionPath().getLastPathComponent();
	}

	private void makeNodeVisible(TreeNode node) {

		TreeNode[] nodes = this.treeModel.getPathToRoot(node);
		TreePath path = new TreePath(nodes);
		this.makeNodeVisible(path);
	}

	private void makeNodeVisible(TreePath path) {

		this.tree.makeVisible(path);
	}

	private void buildFileItemNode(FileInclusionNode node) {

		node.removeAllChildren();
		File file = node.getFile();
		if (file.exists() && file.isDirectory()) {

			File[] sFiles = file.listFiles();
			for (File sFile : sFiles) {
				this.addFileToNode(node, sFile);
			}
		}
	}

	private void addFileToNode(DefaultMutableTreeNode parent, File file) {

		if (!file.exists()) {
			return;
		}

		FileInclusionNode subNode = new FileInclusionNode(new Inclusion(file.getName(), Inclusion.FILE, file.getAbsolutePath()));
		parent.add(subNode);
		System.out.println("\t" + file + " is added.");

		if (file.isDirectory()) {
			System.out.println("listing: " + file);
			File[] subFiles = file.listFiles();
			for (File subFile : subFiles) {
				this.addFileToNode(subNode, subFile);
			}
		}
	}

	private void sortFileItemNode(DefaultMutableTreeNode node) {

		if (node.isLeaf()) {
			return;
		}

		int cnt = node.getChildCount();
		List<TreeNode> dirNodes = new LinkedList<TreeNode>();
		List<TreeNode> leafNodes = new LinkedList<TreeNode>();
		for (int i = 0; i < cnt; i++) {
			TreeNode sNode = node.getChildAt(i);
			if (!sNode.isLeaf()) {
				dirNodes.add(sNode);
				this.sortFileItemNode((DefaultMutableTreeNode) sNode);
			}
			else {
				leafNodes.add(sNode);
			}
		}
		node.removeAllChildren();
		for (TreeNode n : dirNodes) {
			node.add((MutableTreeNode) n);
		}
		for (TreeNode n : leafNodes) {
			node.add((MutableTreeNode) n);
		}
	}

	private LibraryNode libraryToNode(Library library) {

		int cnt = this.rootNode.getChildCount();
		for (int i = 0; i < cnt; i++) {
			LibraryNode node = (LibraryNode) this.rootNode.getChildAt(i);
			if (node.getLibrary().equals(library)) {
				return node;
			}
		}
		

		return null;
	}

	private InclusionNode inclusionToNode(Inclusion inclusion){

		Enumeration nodeIterator = this.rootNode.breadthFirstEnumeration();

		while(nodeIterator.hasMoreElements()){
			Object node =  nodeIterator.nextElement();
			if(node instanceof InclusionNode){
				InclusionNode iNode = (InclusionNode) node;
				if(inclusion.equals(iNode.getInclusion())){
					return iNode;
				}
			}
		}

		return null;
	}

	private InclusionNode inclusionToNode(LibraryNode libNode, Inclusion inclusion){

		Enumeration nodeIterator = libNode.breadthFirstEnumeration();

		while(nodeIterator.hasMoreElements()){
			Object node =  nodeIterator.nextElement();
			if(node instanceof InclusionNode){
				InclusionNode iNode = (InclusionNode) node;
				if(inclusion.equals(iNode.getInclusion())){
					return iNode;
				}
			}
		}

		return null;
	}

	private Library pathToLibrary(TreePath path){

		MutableTreeNode node = (MutableTreeNode) path.getLastPathComponent();
		if(node instanceof LibraryNode){

			return ((LibraryNode)node).getLibrary();
		}

		return null;
	}

	@Override
	protected void initComponents() {

		super.initComponents();

		this.popBuilder = new LibraryPanePopupMenuBuilder(this);
		this.rootNode = new DefaultMutableTreeNode("Libraries");
		this.tree = new JTree(this.rootNode);

		this.setLayout(new BorderLayout());

		this.tree.setRootVisible(true);
		this.tree.addMouseListener(new TreeMouseListener());
		this.tree.setCellRenderer(new LibraryCellRenderer());
		this.add(new JScrollPane(this.tree));

		this.treeModel = (DefaultTreeModel) this.tree.getModel();
		this.makeNodeVisible(EmptyTreeNode.EMPTY_TREE_NODE);
	}

	private class TreeMouseListener extends MouseAdapter {

		@Override
		public void mousePressed(MouseEvent e) {

			int selRow = LibraryPane.this.tree.getRowForLocation(e.getX(), e.getY());
			TreePath selPath = LibraryPane.this.tree.getPathForLocation(e.getX(), e.getY());

			// 在树节点上双击时
			if (selRow != -1 && e.getClickCount() == 2) {

				Object baseObj = LibraryPane.this.getObjectForLocation(e.getX(), e.getY());
				if(baseObj instanceof Inclusion){
					Inclusion inclusion = (Inclusion) baseObj;

					// 如果是文件对象
					if(LibraryUtils.isFileInclusion(inclusion)){

						File file = new File(inclusion.getHref());
						if(file.exists()&&!file.isDirectory()){
							if(ApplicationContext.isFileContentTypeSupported(file)){
								ViewContext.getMainFrame().createNewEditor(file);
							}
							else{
								try {
									NotepadUtils.openFileInSystem(file);
								}
								catch (IOException e1) {
									ViewUtils.showErrorMessageDialog(null, e1.getMessage());
								}
							}

						}
					}
					// 如果是URL对象
					else {
						try {
							URL url = new URL(inclusion.getHref());
							NotepadUtils.browseURL(url);
						}
						catch (MalformedURLException e1) {
							ViewUtils.showErrorMessageDialog(null, e1.getMessage());
						}
						catch (IOException e1) {
							ViewUtils.showErrorMessageDialog(null, e1.getMessage());
						}
						catch (URISyntaxException e1) {
							ViewUtils.showErrorMessageDialog(null, e1.getMessage());
						}
					}

				}
			}

			// 点击右键时
			if (SwingUtilities.isRightMouseButton(e)) {

				// 如果点击点的节点没有被选中则执行选择并清除其他选择
				if (!LibraryPane.this.tree.isPathSelected(selPath)) {
					LibraryPane.this.tree.setSelectionPath(selPath);
				}

				LibraryPane.this.popBuilder.showPopup((Component) e.getSource(), e.getX(), e.getY());
			}
		}
	}

	private List<Library> libraries;
	private DefaultMutableTreeNode rootNode;
	private DefaultTreeModel treeModel;
	private JTree tree;
	private LibraryPanePopupMenuBuilder popBuilder;
	private LibrariesManager librariesManager;
	private ExitActionHandler exitActionHandler;
}
